package com.kepler.social.app.cases;

import com.kepler.social.app.port.UserPasswordChangePort;
import com.kepler.social.common.Time;
import com.kepler.social.common.constants.BusinessState;
import com.kepler.social.common.constants.RedisKeyConst;
import com.kepler.social.common.constants.TipsConst;
import com.kepler.social.common.exception.ServiceException;
import com.kepler.social.common.rule.SmsRule;
import com.kepler.social.common.utils.RandomUtil;
import com.kepler.social.common.utils.StringExtendsUtils;
import com.kepler.social.common.utils.ValidateAssert;
import com.kepler.social.domain.support.sms.SmsBo;
import com.kepler.social.domain.user.account.consts.CommonConst;
import com.kepler.social.domain.user.security.UserSecurityAggregate;
import com.kepler.social.domain.user.security.UserSecurityDomainService;
import com.kepler.social.domain.user.security.UserSecurityRepository;
import com.kepler.social.generic.MailHelper;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;
import org.thymeleaf.TemplateEngine;
import org.thymeleaf.context.Context;

import java.util.concurrent.TimeUnit;

/**
 * 用户账号相关服务
 * 相当于是一个子领域，根据领域的动作可以分为多个userCase, 如: UserLoginCase
 */
@Service
@Slf4j
public class UserPasswordChangeCase implements UserPasswordChangePort {

    @Resource
    private MailHelper mailHelper;
    @Resource
    private RedissonClient redissonClient;
    /**
     * 注意 用的是一套密码 如果变了 一定要改变
     * kepler-social-web com.kepler.social.web.configuration.SecuritySubUnitConfig#passwordEncoder()
     */
    @Resource
    private PasswordEncoder passwordEncoder;
    @Resource
    private RedisTemplate redisTemplate;
    @Resource
    private TemplateEngine templateEngine;
    @Resource
    private SmsBo smsBo;
    @Resource
    private UserSecurityDomainService userSecurityDomainService;
    @Resource
    private UserSecurityRepository userSecurityRepository;

    @Override
    public void sendRetrievePasswordMobilePhoneCaptcha(Integer countryCallingCode, final Long mobilePhoneNumber) {
        log.info("sendRetrievePasswordMobilePhoneCaptcha param phoneNumber {}", mobilePhoneNumber);

        smsBo.sendMobilePhoneCaptcha(SmsRule.defaultCcc(countryCallingCode),
                mobilePhoneNumber, new Time(CommonConst.expireTimeCaptcha,TimeUnit.SECONDS),
                () -> {
                    UserSecurityAggregate aggregate =
                            userSecurityRepository.findByMobilePhone(
                                    SmsRule.defaultCcc(countryCallingCode),
                                    mobilePhoneNumber
                            );
                    aggregate.checkMobilePhoneInUse();
                }
        );
    }

    @Override
    public void changePasswordByMobilePhone(Integer countryCallingCode, Long mobilePhoneNumber, Integer captcha,
                                            String password) {
        ValidateAssert.notNull(captcha, TipsConst.captcha_missing);
        ValidateAssert.isTrue(StringExtendsUtils.isMobilePhoneNumber(mobilePhoneNumber + ""),
                TipsConst.incorrect_mobile_phone_format);
        String lockKey = RedisKeyConst.PHONE_CAPTCHA_LOCK_PREFIX + mobilePhoneNumber;
        final RLock lock = redissonClient.getLock(lockKey);
        try {
            boolean res = lock.tryLock();
            if (!res) {
                throw new ServiceException(BusinessState.DUPLICATE_REQUESTS);
            }
            countryCallingCode = SmsRule.defaultCcc(countryCallingCode);
            // 校验验证码
            smsBo.verifySmsCaptcha(countryCallingCode, mobilePhoneNumber, captcha, false);
            // 查找手机账号
            userSecurityDomainService.resetPasswordByPhone(countryCallingCode, mobilePhoneNumber,
                    passwordEncoder.encode(password));
            // 将新密码发送到手机
            // todo 现在的修改是重置，之后需要改成用户自己设置的密码，底下的发送消息需要删除
            smsBo.sendPasswordChangeSuccessSms(countryCallingCode, mobilePhoneNumber, password,
                    passwordEncoder.encode(password));
        } finally {
            if (null != lock && lock.isLocked()) { // 是否还是锁定状态
                if (lock.isHeldByCurrentThread()) {//是否是当前执行线程的锁
                    lock.unlock(); // 释放锁
                }
            }
        }
    }

    @Override
    public void sendRetrievePasswordEmailCaptcha(String email) {
        ValidateAssert.isTrue(StringExtendsUtils.isEmail(email), TipsConst.incorrect_email_format);
        int captcha = RandomUtil.generateRandomNumber(4);
        redisTemplate.opsForValue().set(RedisKeyConst.EMAIL_CAPTCHA_PREFIX + email, captcha,
                CommonConst.expireTimeCaptcha, TimeUnit.SECONDS);
        Context context = new Context();
        context.setVariable("captcha", captcha);
        String mail = templateEngine.process("captcha_mail_template.html", context);
        mailHelper.sendHtmlMail(email, "验证码", mail);
    }

    @Override
    public void changePasswordByEmail(String email, Integer captcha, String password) {
        log.info("changePasswordByEmail email {} captcha {}", email, captcha);
        ValidateAssert.notNull(captcha, TipsConst.captcha_missing);
        ValidateAssert.isTrue(StringExtendsUtils.isEmail(email), TipsConst.incorrect_email_format);
        ValidateAssert.isTrue(StringExtendsUtils.isNotBlank(password), TipsConst.empty_password);

        Integer captchaCached = (Integer) redisTemplate.opsForValue().get(RedisKeyConst.EMAIL_CAPTCHA_PREFIX + email);
        ValidateAssert.notNull(captchaCached, TipsConst.captcha_expire);
        ValidateAssert.isTrue(captchaCached.equals(captcha), TipsConst.captcha_mismatching);
        // 分布式锁定
        final RLock lock = redissonClient.getLock(RedisKeyConst.MAIL_LOCK_PREFIX + email);
        try {
            boolean res = lock.tryLock();
            if (!res) {
                throw new ServiceException(BusinessState.DUPLICATE_REQUESTS);
            }
            userSecurityDomainService.resetPasswordByEmail(email, passwordEncoder.encode(password));
            redisTemplate.delete(RedisKeyConst.EMAIL_CAPTCHA_PREFIX + email);
        } finally {
            if (null != lock && lock.isLocked()) { // 是否还是锁定状态
                if (lock.isHeldByCurrentThread()) {//是否是当前执行线程的锁
                    lock.unlock(); // 释放锁
                }
            }
        }
    }
}
